#ifndef GRAPHICS_MATERIAL_H
#define GRAPHICS_MATERIAL_H

#include <graphics/node.h>
#include <math/math.h>
#include <render/shader.h>
#include <core/core.h>
#include <graphics/camera.h>
#include <graphics/shaderuniforms.h>

namespace yam3d
{
    namespace render
    {
        class Texture2D;
    }

namespace graphics
{

    class RenderState : public core::Object
    {
    public:
        enum CullMode
        {
            CULL_BACK = 0,
            CULL_FRONT,
            CULL_NONE
        };

        RenderState();
        virtual ~RenderState();

        void setCullMode(CullMode cullMode);
        void setDepthEnable(bool depthEnable);

        CullMode getCullMode()const;
        bool isDepthEnabled() const;

    private:
        CullMode    m_cullMode;
        bool        m_depthEnable;

        RenderState(const RenderState&);
        RenderState& operator=(const RenderState&);
    };

    class SamplerState : public core::Object
    {
    public:
        SamplerState();
        virtual ~SamplerState();

        enum Wrapping
        {
            REPEAT = 0,
            CLAMP
        };

        enum Filtering
        {
            NEAREST = 0,
            LINEAR,
            NEAREST_MIPMAP_NEAREST,
            NEAREST_MIPMAP_LINEAR,
            LINEAR_MIPMAP_NEAREST,
            LINEAR_MIPMAP_LINEAR
        };

        void setWrapping(Wrapping wrapS, Wrapping wrapT, Wrapping wrapR);
        void setFiltering( Filtering minifyFilter, Filtering magnifyFilter );

        Wrapping getWrappingS() const;
        Wrapping getWrappingT() const;
        Wrapping getWrappingR() const;

        Filtering getMinifyFilter() const;
        Filtering getMagnifyFilter() const;

    private:
        Wrapping m_wrapS;
        Wrapping m_wrapT;
        Wrapping m_wrapR;
        Filtering m_minifyFilter;
        Filtering m_magnifyFilter;
    };

    class Pass : public core::Object
    {
    public:
        Pass();
        virtual ~Pass();

    //    void setCamera( const math::mat4& cameraTransform, const math::mat4& cameraProjection );
        void setCameraRef( const core::string& cameraRef ) { m_cameraRef = cameraRef; }
        void setShaderUniforms( graphics::ShaderUniforms* shaderUniforms);
        void setShaderProgram( render::ShaderProgram* shaderProgram );
        void setRenderState(RenderState* renderState);
        void setTexture(int index, const core::string& name, render::Texture* texture);
        void setSamplerState(int index, SamplerState* samplerState);

        RenderState* getRenderState();
        SamplerState* getSamplerState(int index);

        const core::string& getCameraRef() const
        {
            return m_cameraRef;
        }
      //  graphics::Camera* getCamera() const;
     //   const math::mat4& getCameraTransform() const { return m_cameraTransform; }
     //   const math::mat4& getCameraProjection() const { return m_cameraProjection; }

        graphics::ShaderUniforms* getShaderUniforms() const;
        render::ShaderProgram* getShaderProgram() const;
        int getSamplerCount() const;
        const RenderState* getRenderState() const;

        const SamplerState* getSamplerState(int index) const;
        render::Texture* getTexture(int index) const;
        const core::string& getSamplerName(int index) const;


    private:
        struct SamplerStage
        {
            SamplerStage() : name(""), samplerState(0), texture(0) {}
            ~SamplerStage() {}

            core::string name;
            core::Ref<SamplerState> samplerState;
            core::Ref<render::Texture> texture;
        };

        core::Ref<graphics::RenderState>      m_renderState;
        core::vector<SamplerStage>  m_samplerStages;
        core::Ref<render::ShaderProgram>      m_shader;
        core::Ref<graphics::ShaderUniforms>   m_shaderUniforms;
     //   graphics::Camera*           m_camera;
        core::string m_cameraRef;
   //     math::mat4 m_cameraTransform;
   //     math::mat4 m_cameraProjection;


        Pass(const Pass&);
        Pass& operator=(const Pass&);
    };


    class Effect : public core::Object
    {
    public:
        Effect();
        virtual ~Effect();

        void setPass(int index, Pass* pass);
        int getPassCount() const;

        Pass* getPass(int index) const;

    private:
  //      core::Ref<graphics::ShaderUniforms>   m_shaderUniforms;
        core::vector< core::Ref<Pass> >  m_passes;

        Effect(const Effect&);
        Effect& operator=(const Effect&);
    };


    namespace Internal
    {
        struct Effect;
        struct EffectGroup;
        struct Pass;
        struct Shader;
        struct ShaderProgram;
        struct RenderState;
        struct SamplerState;
        struct ShaderUniforms;
    }

    class EffectFile : public core::Object
    {
    public:
        EffectFile( const core::string& fileName );
        virtual ~EffectFile();

        int getEffectCount() const;
        Effect* getEffect( int index );
        Effect* getEffect(const core::string& name);

    private:
        core::Ref<Internal::EffectGroup> effectGroup;
     //   graphics::Camera* m_mainCamera;

        void create(Internal::Effect* effect);
        void create(Internal::Pass* pass);
        void create(Internal::Shader* shader);
        void create(Internal::ShaderProgram* shaderProgram);
        void create(Internal::RenderState* renderState);
        void create(Internal::SamplerState* samplerState);
        void create(Internal::ShaderUniforms* shaderUniforms);

       // graphics::Camera* getCameraRef(const core::string& refName);
        graphics::RenderState* getRenderStateRef(const core::string& refName);
        render::ShaderProgram* getShaderProgramRef(const core::string& refName);
        render::Texture2D* getTextureRef(const core::string& refName);
        graphics::SamplerState* getSamplerStateRef(const core::string& refName);
        graphics::ShaderUniforms* getShaderUniformsRef(const core::string& refName);

        EffectFile();
        EffectFile(const EffectFile&);
        EffectFile& operator=(const EffectFile&);
    };

}
}

#endif // GRAPHICS_MATERIAL_H
